package sketch.main;

import java.io.File;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

//import org.apache.commons.collections.TestAll;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;

import sketch.ast.ASTUtils;
import sketch.instrumenter.SourceCodeInstrumenter;
import sketch.instrumenter.Tracer;
import sketch.instrumenter.TracerFileReader;
import sketch.ounit.OUnit;
import sketch.ounit.ObservationInstantiationVisitor;
import sketch.specs.annotation.Repl;
import sketch.specs.annotation.Repls;
import sketch.specs.annotation.TestSketch;
import sketch.util.FileListing;
import sketch.util.Globals;
import sketch.util.SketchUtil;

public class GeneratingTestsBatch {

	/**
	 * Given a source directory, the algorithm works as follows:
	 * 1. first read all files inside it, then parses each file into compilation unit.
	 * 2. for each file, do source code instrumentation, then put all instrumented files into
	 *    a specific folder <code>GeneratingTests.tmp_dir_instrumentation</code>.
	 * 3. run the <mainEntryJUnitClass>, it will keep all recorded value <code>into Tracer.traceFile</code>
	 * 4. parse the tracer file, and re-write the compilation unit generated at step 1, and output
	 *    the final test results to <code>output_dir</code>.
	 * */
	public static void obtainRuntimeInfoAndOutputTests(String source_dir, String output_dir, String mainEntryJUnitClass,
			String additional_class_path) {
		
		//clear the trace file
		Tracer.clearFile();
		
		List<File> all_files = FileListing.getFiles(new File(source_dir));
		List<CompilationUnit> units = new LinkedList<CompilationUnit>();
		Map<CompilationUnit, File> unit_file = new LinkedHashMap<CompilationUnit, File>();
		//List<CompilationUnit> instrumented_units = new LinkedList<CompilationUnit>();
		List<File> file_to_compile = new LinkedList<File>();
		for(File file : all_files) {
			String file_path = file.getAbsolutePath();
			if(file_path.trim().endsWith(".java")) {
			  CompilationUnit unit = ASTUtils.astRoot(file_path);
			  units.add(unit);
			  unit_file.put(unit, file);
			  //start to do instrumentation
			  CompilationUnit instrumented_copy = ASTUtils.deepClone(unit);
			  SourceCodeInstrumenter.change_name = false;
			  SourceCodeInstrumenter instrumenter = new SourceCodeInstrumenter(instrumented_copy);
			  instrumenter.doInstrumentation();
			  //System.out.println("has instrumented? " + instrumenter.hasInstrumentedTheCode());
			  //output the instrumented file
			  String dirStructure = SketchUtil.transClassNameDotToSlash(unit.getPackage().getName().toString());
			  SketchUtil.mkdirs(SketchTestOptions.output_dir + Globals.fileSep + dirStructure);
			  String instrumentation_dir = SketchTestOptions.output_dir  + Globals.fileSep + dirStructure
			      + Globals.fileSep + file.getName();
			  //the file after instrumentation
			  File instrumetned_file = new File(instrumentation_dir);
			  ASTUtils.outputUnit(instrumented_copy, instrumetned_file);
			 // instrumented_units.add(instrumented_copy);
			  file_to_compile.add(instrumetned_file);
			}
		}
		
		//start to compile and run
		CodeToolkit.compile(file_to_compile, additional_class_path);
		
		//run the junit tests
		System.out.println("Look up JUnit class, and execute JUnit: " + mainEntryJUnitClass);
		Class<?> testClass = CodeToolkit.loadClassFromDir(SketchTestOptions.output_dir  , mainEntryJUnitClass);
		//junit.textui.TestRunner.run(testClass);
		String[] testCaseNames = { testClass.getName() };
//		junit.textui.TestRunner.main(testCaseNames);
		CodeToolkit.runUniTests(testCaseNames);
		
		System.out.println("Finish executing JUnit tests, and start rewriting!");
		TracerFileReader reader = new TracerFileReader(Tracer.saveFile);
		Map<String, Map<Integer, String>> parsingResults = reader.parse();
		
		System.out.println("\nthe long parsing results.");
		System.out.println(parsingResults);
		for(CompilationUnit unit : units) {
			ObservationInstantiationVisitor visitor = new ObservationInstantiationVisitor(unit, parsingResults); 
			unit.accept(visitor);
			
			//remove annotations
			if(GeneratingTests.release) {
				List<MethodDeclaration> methods = ASTUtils.getAllMethods(unit);
				for(MethodDeclaration method : methods) {
					ASTUtils.removeMethodAnnotation(method, TestSketch.class);
					ASTUtils.removeMethodAnnotation(method, OUnit.class);
					ASTUtils.removeMethodAnnotation(method, Repls.class);
					ASTUtils.removeMethodAnnotation(method, Repl.class);
				}
			}
			
			//output to the dir
			String dirStructure = SketchUtil.transClassNameDotToSlash(unit.getPackage().getName().toString());
			SketchUtil.mkdirs(output_dir + Globals.fileSep + dirStructure);
			String outputFileAbsolutePath = output_dir + Globals.fileSep + dirStructure
			    + Globals.fileSep + unit_file.get(unit).getName();
			ASTUtils.outputUnit(unit, outputFileAbsolutePath);
		}
		
		
		//reclaim the memory
		all_files.clear();
		units.clear();
		unit_file.clear();
		file_to_compile.clear();
	}
	
}